﻿// <copyright file="Translator.cs" company="QUT">
//     Copyright (c) Ninety Nine Percent. All rights reserved.
// </copyright>

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;

using Google.KML;
using Qut.GenerateKml.Data;

namespace Qut.GenerateKml.Business
{
    /// <summary>
    /// This class will accept sociological and geographical data as input and translate this information into the form of a Kml Document.
    /// </summary>
    public class Translator : ITranslator
    {
        #region ITranslator Members

        /// <summary>
        /// The party / colour mappings.
        /// </summary>
        private Dictionary<string, Color> _colours = new Dictionary<string, Color>();

        /// <summary>
        /// Adds a colour.
        /// </summary>
        /// <param name="key">The supplied key.</param>
        /// <param name="value">The supplied value.</param>
        public void AddColourMapping(string key, Color value)
        {
            this._colours.Add(key, value);
        }

        /// <summary>
        /// Translates the specified geographical data into a Kml document.
        /// </summary>
        /// <param name="geographicalData">The geographical data.</param>
        /// <returns>Returns the Google Earth Document.</returns>
        public Google.KML.geDocument Translate(MapInfoFile geographicalData)
        {
            geDocument doc = new geDocument();
            doc.Name = "KML";

            geStyle style = new geStyle("Random");
            geColor colour = new geColor();
            colour.SysColor = Color.Red;
            style.PolyStyle = new gePolyStyle();
            style.PolyStyle.ColorMode = geColorModeEnum.random;
            style.PolyStyle.Fill = true;
            style.PolyStyle.Outline = true;

            DataTable midData = geographicalData.GetMidData();
            Hashtable midHeader = geographicalData.GetMifHeader();

            int regionCount = 0;
            foreach (MapInfoRegion region in geographicalData.GetMifData())
            {
                gePolygon poly = null;
                int i = 0;
                foreach (MapInfoPolygon polygon in region.GetPolygons())
                {
                    i++;
                    List<geCoordinates> coords = new List<geCoordinates>();
                    int coordCount = 0;
                    foreach (PointF point1 in polygon.GetCoordinates())
                    {
                        if (coordCount % 4 != 0 || coordCount == polygon.GetCoordinates().Count || polygon.GetCoordinates().Count < 30000)
                        {
                            geCoordinates c = new geCoordinates(new geAngle90(Convert.ToDouble(point1.Y)), new geAngle180(Convert.ToDouble(point1.X)));
                            coords.Add(c);
                        }

                        coordCount++;
                    }

                    geLinearRing lring = new geLinearRing(coords);
                    lring.ID = i.ToString();
                    geOuterBoundaryIs outer = new geOuterBoundaryIs(lring);
                    poly = new gePolygon(outer);
                    poly.Extrude = true;
                    poly.AltitudeMode = geAltitudeModeEnum.clampToGround;
                    poly.Tessellate = true;
                    gePlacemark pm = new gePlacemark();
                    pm.StyleUrl = "#Random";
                    pm.Geometry = poly;
                    pm.ID = i.ToString();
                    pm.Name = midData.Rows[regionCount][1].ToString();
                    string html = this.GenerateHtmlHeader();
                    for (int j = 0; j < midData.Columns.Count; j++)
                    {
                        html += this.GenerateHtmlContentBold(midData.Columns[j].ColumnName.Trim(), midData.Rows[regionCount][j].ToString());
                    }

                    html += this.GenerateHtmlFooter();
                    pm.Description = html;
                    doc.Features.Add(pm);
                }

                regionCount++;
            }

            doc.StyleSelectors.Add(style);

            return doc;
        }

        /// <summary>
        /// Translates the specified geographical data.
        /// </summary>
        /// <param name="geographicalData">The geographical data.</param>
        /// <param name="columns">The columns.</param>
        /// <returns>The translated document.</returns>
        public Google.KML.geDocument Translate(MapInfoFile geographicalData, List<string> columns)
        {
            geDocument doc = new geDocument();
            doc.Name = "KML";

            geStyle styleALP1 = this.SetStyle("ALP1", this._colours["ALP"], 255);
            geStyle styleALP2 = this.SetStyle("ALP2", this._colours["ALP"], 225);
            geStyle styleALP3 = this.SetStyle("ALP3", this._colours["ALP"], 195);
            geStyle styleALP4 = this.SetStyle("ALP4", this._colours["ALP"], 165);
            geStyle styleALP5 = this.SetStyle("ALP5", this._colours["ALP"], 135);
            geStyle styleLP1 = this.SetStyle("LP1", this._colours["LP"], 255);
            geStyle styleLP2 = this.SetStyle("LP2", this._colours["LP"], 225);
            geStyle styleLP3 = this.SetStyle("LP3", this._colours["LP"], 195);
            geStyle styleLP4 = this.SetStyle("LP4", this._colours["LP"], 165);
            geStyle styleLP5 = this.SetStyle("LP5", this._colours["LP"], 135);
            geStyle styleNP1 = this.SetStyle("NP1", this._colours["NP"], 255);
            geStyle styleNP2 = this.SetStyle("NP2", this._colours["NP"], 225);
            geStyle styleNP3 = this.SetStyle("NP3", this._colours["NP"], 195);
            geStyle styleNP4 = this.SetStyle("NP4", this._colours["NP"], 165);
            geStyle styleNP5 = this.SetStyle("NP5", this._colours["NP"], 135);
            geStyle styleDEM1 = this.SetStyle("DEM1", this._colours["DEM"], 255);
            geStyle styleDEM2 = this.SetStyle("DEM2", this._colours["DEM"], 225);
            geStyle styleDEM3 = this.SetStyle("DEM3", this._colours["DEM"], 195);
            geStyle styleDEM4 = this.SetStyle("DEM4", this._colours["DEM"], 165);
            geStyle styleDEM5 = this.SetStyle("DEM5", this._colours["DEM"], 135);
            geStyle styleGRN1 = this.SetStyle("GRN1", this._colours["GRN"], 255);
            geStyle styleGRN2 = this.SetStyle("GRN2", this._colours["GRN"], 225);
            geStyle styleGRN3 = this.SetStyle("GRN3", this._colours["GRN"], 195);
            geStyle styleGRN4 = this.SetStyle("GRN4", this._colours["GRN"], 165);
            geStyle styleGRN5 = this.SetStyle("GRN5", this._colours["GRN"], 135);
            geStyle styleOTH1 = this.SetStyle("OTH1", this._colours["OTH"], 255);
            geStyle styleOTH2 = this.SetStyle("OTH2", this._colours["OTH"], 225);
            geStyle styleOTH3 = this.SetStyle("OTH3", this._colours["OTH"], 195);
            geStyle styleOTH4 = this.SetStyle("OTH4", this._colours["OTH"], 165);
            geStyle styleOTH5 = this.SetStyle("OTH5", this._colours["OTH"], 135);

            DataTable midData = geographicalData.GetMidData();
            Hashtable midHeader = geographicalData.GetMifHeader();
            int regionCount = 0;

            using (var sociological = new SociologicalData())
            {
                geFolder brisbaneNorthFolder = new geFolder();
                brisbaneNorthFolder.ID = "Brisbane North Region";
                brisbaneNorthFolder.Name = "Brisbane North Region";
                geFolder brisbaneSouthFolder = new geFolder();
                brisbaneSouthFolder.ID = "Brisbane South Region";
                brisbaneSouthFolder.Name = "Brisbane South Region";
                geFolder cairnFolder = new geFolder();
                cairnFolder.ID = "Cairns Region";
                cairnFolder.Name = "Cairns Region";
                geFolder darlingDownsFolder = new geFolder();
                darlingDownsFolder.ID = "Darling Downs Region";
                darlingDownsFolder.Name = "Darling Downs Region";
                geFolder goldCoastFolder = new geFolder();
                goldCoastFolder.ID = "Gold Coast Region";
                goldCoastFolder.Name = "Gold Coast Region";
                geFolder harveyBayFolder = new geFolder();
                harveyBayFolder.ID = "Harvey Bay Region";
                harveyBayFolder.Name = "Harvey Bay Region";
                geFolder ipswichFolder = new geFolder();
                ipswichFolder.ID = "Ipswich Region";
                ipswichFolder.Name = "Ipswich Region";
                geFolder mackayFolder = new geFolder();
                mackayFolder.ID = "Mackay Region";
                mackayFolder.Name = "Mackay Region";
                geFolder rockhamptonFolder = new geFolder();
                rockhamptonFolder.ID = "Rockhampton Region";
                rockhamptonFolder.Name = "Rockhampton Region";
                geFolder sunshineCoastFolder = new geFolder();
                sunshineCoastFolder.ID = "Sunshine Coast Region";
                sunshineCoastFolder.Name = "Sunshine Coast Region";
                geFolder townsvilleFolder = new geFolder();
                townsvilleFolder.ID = "Townsville Region";
                townsvilleFolder.Name = "Townsville Region";
                geFolder westQueenslandFolder = new geFolder();
                westQueenslandFolder.ID = "West Queensland Region";
                westQueenslandFolder.Name = "West Queensland Region";

                foreach (MapInfoRegion region in geographicalData.GetMifData())
                {
                    geFolder folder = new geFolder();
                    gePolygon poly = null;
                    int i = 0;
                    string winner = string.Empty;
                    double alp = 0.0;
                    double lp = 0.0;
                    double np = 0.0;
                    double dem = 0.0;
                    double grn = 0.0;
                    double oth = 0.0;
                    double tppalp = 0.0;
                    double tpplp = 0.0;
                    var frRows = from r in sociological.Database.QldFederalResultsByElectorate2004s
                                 where r.Division == midData.Rows[regionCount][1].ToString()
                                 select r;
                    foreach (var frRow in frRows)
                    {
                        double largest = 0.0;
                        if (frRow.ALP > largest)
                        {
                            winner = "ALP";
                            largest = (double)frRow.ALP;
                        }

                        if (frRow.LP > largest)
                        {
                            winner = "LP";
                            largest = (double)frRow.LP;
                        }

                        if (frRow.NP > largest)
                        {
                            winner = "NP";
                            largest = (double)frRow.NP;
                        }

                        if (frRow.DEM > largest)
                        {
                            winner = "DEM";
                            largest = (double)frRow.DEM;
                        }

                        if (frRow.GRN > largest)
                        {
                            winner = "GRN";
                            largest = (double)frRow.GRN;
                        }

                        if (frRow.OTH > largest)
                        {
                            winner = "OTH";
                            largest = (double)frRow.OTH;
                        }

                        alp = (double)frRow.ALP;
                        lp = (double)frRow.LP;
                        np = (double)frRow.NP;
                        dem = (double)frRow.DEM;
                        grn = (double)frRow.GRN;
                        oth = (double)frRow.OTH;
                        tppalp = (double)frRow.TPPALP;
                        tpplp = (double)frRow.TPPLP;
                    }

                    double safety = this.CalculateSafety(midData.Rows[regionCount][1].ToString(), winner, sociological);
                    string safetyHTML = null;
                    if (safety > 25.0)
                    {
                        safetyHTML = this.GenerateHtmlContent("Seat Safety: ", "<span style=\"color:blue;\">Rock Solid</span>");
                        winner += "1";
                    }
                    else if (safety > 15.0)
                    {
                        safetyHTML = this.GenerateHtmlContent("Seat Safety: ", "<span style=\"color:green;\">Very Safe</span>");
                        winner += "2";
                    }
                    else if (safety > 10.0)
                    {
                        safetyHTML = this.GenerateHtmlContent("Seat Safety: ", "<span style=\"color:green;\">Safe</span>");
                        winner += "3";
                    }
                    else if (safety > 5.0)
                    {
                        safetyHTML = this.GenerateHtmlContent("Seat Safety: ", "<span style=\"color:red;\">Moderately Safe</span>");
                        winner += "4";
                    }
                    else
                    {
                        safetyHTML = this.GenerateHtmlContent("Seat Safety: ", "<span style=\"color:red;\">Marginal Seat</span>");
                        winner += "5";
                    }

                    folder.ID = midData.Rows[regionCount][1].ToString();
                    folder.Name = midData.Rows[regionCount][1].ToString();
                    folder.Description = this.GenerateHtmlHeader();
                    folder.Description += safetyHTML;
                    folder.Description += this.GenerateHtmlFooter();
                    foreach (MapInfoPolygon polygon in region.GetPolygons())
                    {
                        i++;
                        List<geCoordinates> coords = new List<geCoordinates>();
                        int coordCount = 0;
                        foreach (PointF point1 in polygon.GetCoordinates())
                        {
                            if (coordCount % 4 != 0 || coordCount == polygon.GetCoordinates().Count || polygon.GetCoordinates().Count < 30000)
                            {
                                geCoordinates c = new geCoordinates(new geAngle90(Convert.ToDouble(point1.Y)), new geAngle180(Convert.ToDouble(point1.X)));
                                coords.Add(c);
                            }

                            coordCount++;
                        }

                        geLinearRing lring = new geLinearRing(coords);
                        lring.ID = i.ToString();
                        geOuterBoundaryIs outer = new geOuterBoundaryIs(lring);
                        poly = new gePolygon(outer);
                        poly.Extrude = true;
                        poly.AltitudeMode = geAltitudeModeEnum.clampToGround;
                        poly.Tessellate = true;
                        gePlacemark pm = new gePlacemark();
                        pm.Name = midData.Rows[regionCount][1].ToString();
                        pm.StyleUrl = "#" + winner;
                        pm.Geometry = poly;
                        pm.ID = i.ToString();
                        string html = this.GenerateHtmlHeader();
                        html += safetyHTML;
                        for (int j = 0; j < midData.Columns.Count; j++)
                        {
                            foreach (string col in columns)
                            {
                                if (midData.Columns[j].ColumnName == col)
                                {
                                    html += this.GenerateHtmlContentBold(midData.Columns[j].ColumnName.Trim(), midData.Rows[regionCount][j].ToString());
                                }
                            }
                        }

                        html += this.GenerateHtmlTitle("<br>First Preferences:");
                        html += this.GenerateHtmlContent("Australian Labor Party", alp.ToString() + "%");
                        html += this.GenerateHtmlContent("Liberal", lp.ToString() + "%");
                        html += this.GenerateHtmlContent("The Nationals", np.ToString() + "%");
                        html += this.GenerateHtmlContent("Democrates", dem.ToString() + "%");
                        html += this.GenerateHtmlContent("The Greens", grn.ToString() + "%");
                        html += this.GenerateHtmlContent("Others", oth.ToString() + "%");
                        html += this.GenerateHtmlTitle("Two-Party-Preferred:");
                        html += this.GenerateHtmlContent("Liberal", tpplp.ToString() + "%");
                        html += this.GenerateHtmlContent("Australian Labor Party", tppalp.ToString() + "%");
                        html += this.GenerateHtmlFooter();
                        pm.Description = html;
                        folder.Features.Add(pm);
                    }

                    if (folder.Name == "Brisbane" || folder.Name == "Dickson" || folder.Name == "Lilley" || folder.Name == "Ryan")
                    {
                        brisbaneNorthFolder.Features.Add(folder);
                    }
                    else if (folder.Name == "Bowman" || folder.Name == "Bonner" || folder.Name == "Griffith" || folder.Name == "Moreton" || folder.Name == "Oxley" || folder.Name == "Rankin")
                    {
                        brisbaneSouthFolder.Features.Add(folder);
                    }
                    else if (folder.Name == "Leichhardt")
                    {
                        cairnFolder.Features.Add(folder);
                    }
                    else if (folder.Name == "Forde")
                    {
                        darlingDownsFolder.Features.Add(folder);
                    }
                    else if (folder.Name == "Fadden" || folder.Name == "Moncrieff" || folder.Name == "Mcpherson")
                    {
                        goldCoastFolder.Features.Add(folder);
                    }
                    else if (folder.Name == "Wide Bay")
                    {
                        harveyBayFolder.Features.Add(folder);
                    }
                    else if (folder.Name == "Blair" || folder.Name == "Groom")
                    {
                        ipswichFolder.Features.Add(folder);
                    }
                    else if (folder.Name == "Dawson")
                    {
                        mackayFolder.Features.Add(folder);
                    }
                    else if (folder.Name == "Capricornia" || folder.Name == "Hinkler")
                    {
                        rockhamptonFolder.Features.Add(folder);
                    }
                    else if (folder.Name == "Fairfax" || folder.Name == "Fisher" || folder.Name == "Longman" || folder.Name == "Petrie")
                    {
                        sunshineCoastFolder.Features.Add(folder);
                    }
                    else if (folder.Name == "Herbert")
                    {
                        townsvilleFolder.Features.Add(folder);
                    }
                    else if (folder.Name == "Kennedy" || folder.Name == "Maranoa")
                    {
                        westQueenslandFolder.Features.Add(folder);
                    }
                    else
                    {
                        doc.Features.Add(folder);
                    }
                    
                    regionCount++;
                }

                doc.Features.Add(brisbaneNorthFolder);
                doc.Features.Add(brisbaneSouthFolder);
                doc.Features.Add(cairnFolder);
                doc.Features.Add(darlingDownsFolder);
                doc.Features.Add(goldCoastFolder);
                doc.Features.Add(harveyBayFolder);
                doc.Features.Add(ipswichFolder);
                doc.Features.Add(mackayFolder);
                doc.Features.Add(rockhamptonFolder);
                doc.Features.Add(sunshineCoastFolder);
                doc.Features.Add(townsvilleFolder);
                doc.Features.Add(westQueenslandFolder);
            }

            doc.StyleSelectors.Add(styleALP1);
            doc.StyleSelectors.Add(styleALP2);
            doc.StyleSelectors.Add(styleALP3);
            doc.StyleSelectors.Add(styleALP4);
            doc.StyleSelectors.Add(styleALP5);
            doc.StyleSelectors.Add(styleLP1);
            doc.StyleSelectors.Add(styleLP2);
            doc.StyleSelectors.Add(styleLP3);
            doc.StyleSelectors.Add(styleLP4);
            doc.StyleSelectors.Add(styleLP5);
            doc.StyleSelectors.Add(styleNP1);
            doc.StyleSelectors.Add(styleNP2);
            doc.StyleSelectors.Add(styleNP3);
            doc.StyleSelectors.Add(styleNP4);
            doc.StyleSelectors.Add(styleNP5);
            doc.StyleSelectors.Add(styleDEM1);
            doc.StyleSelectors.Add(styleDEM2);
            doc.StyleSelectors.Add(styleDEM3);
            doc.StyleSelectors.Add(styleDEM4);
            doc.StyleSelectors.Add(styleDEM5);
            doc.StyleSelectors.Add(styleGRN1);
            doc.StyleSelectors.Add(styleGRN2);
            doc.StyleSelectors.Add(styleGRN3);
            doc.StyleSelectors.Add(styleGRN4);
            doc.StyleSelectors.Add(styleGRN5);
            doc.StyleSelectors.Add(styleOTH1);
            doc.StyleSelectors.Add(styleOTH2);
            doc.StyleSelectors.Add(styleOTH3);
            doc.StyleSelectors.Add(styleOTH4);
            doc.StyleSelectors.Add(styleOTH5);
            return doc;
        }

        /// <summary>
        /// Sets the style.
        /// </summary>
        /// <param name="styleName">Name of the style.</param>
        /// <param name="styleColour">The style colour.</param>
        /// <param name="alpha">The alpha value of the colour.</param>
        /// <returns>Returns the Google Earth Style.</returns>
        private geStyle SetStyle(string styleName, Color styleColour, int alpha)
        {
            geStyle style = new geStyle(styleName);
            geColor colour = new geColor();
            colour.SysColor = Color.FromArgb(alpha, styleColour);
            style.PolyStyle = new gePolyStyle();
            style.PolyStyle.Color = colour;
            style.PolyStyle.ColorMode = geColorModeEnum.normal;
            style.PolyStyle.Fill = true;
            style.PolyStyle.Outline = true;

            return style;
        }

        /// <summary>
        /// Generates the HTML header.
        /// </summary>
        /// <returns>Generates the HTML Header.</returns>
        private string GenerateHtmlHeader()
        {
            return "<table style=\"font-size: 80%;\"><colgroup><col /><col /></colgroup>";
        }

        /// <summary>
        /// Generates the HTML content bold.
        /// </summary>
        /// <param name="key">The supplied key.</param>
        /// <param name="value">The supplied value.</param>
        /// <returns>Returns the HTML Content Bold.</returns>
        private string GenerateHtmlContentBold(string key, string value)
        {
            return "<tr style=\"padding-right: 20px; font-weight:bold;\"><td>" + key + "</td><td>" + value + "</td></tr>";
        }

        /// <summary>
        /// Generates the HTML title.
        /// </summary>
        /// <param name="title">The supplied title.</param>
        /// <returns>Returns the HTML title.</returns>
        private string GenerateHtmlTitle(string title)
        {
            return "<tr><td style=\"font-style: italic;\" colspan=\"2\">" + title + "</td></tr>";
        }

        /// <summary>
        /// Generates the content of the HTML.
        /// </summary>
        /// <param name="key">The supplied key.</param>
        /// <param name="value">The supplied value.</param>
        /// <returns>Returns the HTML content.</returns>
        private string GenerateHtmlContent(string key, string value)
        {
            return "<tr><td style=\"padding-right: 20px;\">" + key + "</td><td>" + value + "</td></tr>";
        }

        /// <summary>
        /// Generates the HTML footer.
        /// </summary>
        /// <returns>Returns the HTML Footer.</returns>
        private string GenerateHtmlFooter()
        {
            return "</table>";
        }

        /// <summary>
        /// Calculates the safty.
        /// </summary>
        /// <param name="division">The division.</param>
        /// <param name="lastWinner">The last winner.</param>
        /// <param name="sociological">The sociological.</param>
        /// <returns>The tranlated document.</returns>
        private double CalculateSafety(string division, string lastWinner, SociologicalData sociological)
        {
            double safety = 0.0;
            var federalResults2004Rows = from r in sociological.Database.FederalElectionResultsQld2004s
                                         where r.Seat == division
                                         select r;
            foreach (var federalResults2004Row in federalResults2004Rows)
            {
                safety = Convert.ToDouble(federalResults2004Row.Percentage);

                int consecutiveWins = 2009 - Convert.ToInt32(federalResults2004Row.HeldSince);
                if (consecutiveWins > 4)
                {
                    safety += 10.0;
                }
                else if (consecutiveWins > 2)
                {
                    safety += 5.0;
                }
            }

            var federalStateMappingRows = from r in sociological.Database.QldFederalStateElectorateMappings
                                          where r.FederalSeat == division
                                          select r;
            foreach (var federalStateMappingRow in federalStateMappingRows)
            {
                var stateResults2006Rows = from r in sociological.Database.QldStateResultsByElectorate2006s
                                           where r.DistrictName == federalStateMappingRow.StateSeat
                                           select r;
                foreach (var stateResults2006Row in stateResults2006Rows)
                {
                    string stateWinner = string.Empty;
                    if (Convert.ToDouble(stateResults2006Row.Percentage1) >= Convert.ToDouble(stateResults2006Row.Percentage2))
                    {
                        stateWinner = stateResults2006Row.Party1;
                    }
                    else
                    {
                        stateWinner = stateResults2006Row.Party2;
                    }

                    if ((stateWinner == "ALP" && lastWinner == "ALP") || ((stateWinner == "LIB" || stateWinner == "NPA") && (lastWinner == "LP" || lastWinner == "NP")))
                    {
                        safety += 2.0;
                    }
                    else
                    {
                        safety -= 4.0;
                    }
                }
            }

            return safety;
        }

        #endregion
    }
}
